#include "global.h"
#include "RageUtil_CircularBuffer.h"
#include "RageSoundManager.h"

#include <stdio.h>
#include <stdlib.h>

#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

template <class T> CircBuf<T>::CircBuf()
{
	buf = NULL;
	clear();
}

template <class T> CircBuf<T>::~CircBuf()
{
	delete[] buf;
}
	
/* Return the number of elements available to read. */
template <class T> unsigned CircBuf<T>::num_readable() const
{
	const int rpos = read_pos;
	const int wpos = write_pos;
	if( rpos < wpos )
		/* The buffer looks like "eeeeDDDDeeee" (e = empty, D = data). */
		return wpos - rpos;
	else if( rpos > wpos )
		/* The buffer looks like "DDeeeeeeeeDD" (e = empty, D = data). */
		return size - (rpos - wpos);
	else // if( rpos == wpos )
		/* The buffer looks like "eeeeeeeeeeee" (e = empty, D = data). */
		return 0;
}

/* Return the number of elements writable.  Note that there must always
 * be one */
template <class T> unsigned CircBuf<T>::num_writable() const
{
	const int rpos = read_pos;
	const int wpos = write_pos;

	int ret;
	if( rpos < wpos )
		/* The buffer looks like "eeeeDDDDeeee" (e = empty, D = data). */
		ret = size - (wpos - rpos);
	else if( rpos > wpos )
		/* The buffer looks like "DDeeeeeeeeDD" (e = empty, D = data). */
		ret = rpos - wpos;
	else // if( rpos == wpos )
		/* The buffer looks like "eeeeeeeeeeee" (e = empty, D = data). */
		ret = size;

	/* Subtract one, to account for the element that we never fill. */
	return ret - 1;
}

template <class T> unsigned CircBuf<T>::capacity() const { return size; }

template <class T> void CircBuf<T>::reserve( unsigned n )
{
	clear();
	delete[] buf;
	buf = NULL;

	/* Reserve an extra byte.  We'll never fill more than n bytes; the extra
	 * byte is to guarantee that read_pos != write_pos when the buffer is full,
	 * since that would be ambiguous with an empty buffer. */
	if( n != 0 )
	{
		buf = new T[n+1];
		size = n+1;
	}
	else
		size = 0;
}

template <class T> void CircBuf<T>::clear()
{
	read_pos = write_pos = 0;
}

/* Indicate that n elements have been written. */
template <class T> void CircBuf<T>::advance_write_pointer( int n )
{
	write_pos = (write_pos + n) % size;
}

/* Indicate that n elements have been read. */
template <class T> void CircBuf<T>::advance_read_pointer( int n )
{
	read_pos = (read_pos + n) % size;
}

template <class T> void CircBuf<T>::get_write_pointers( T *pPointers[2], unsigned pSizes[2] )
{
	const int rpos = read_pos;
	const int wpos = write_pos;

	if( rpos <= wpos )
	{
		/* The buffer looks like "eeeeDDDDeeee" or "eeeeeeeeeeee" (e = empty, D = data). */
		pPointers[0] = buf+wpos;
		pPointers[1] = buf;

		pSizes[0] = size - wpos;
		pSizes[1] = rpos;
	}
	else if( rpos > wpos )
	{
		/* The buffer looks like "DDeeeeeeeeDD" (e = empty, D = data). */
		pPointers[0] = buf+wpos;
		pPointers[1] = NULL;

		pSizes[0] = rpos - wpos;
		pSizes[1] = 0;
	}

	/* Subtract one, to account for the element that we never fill. */
	if( pSizes[1] )
		--pSizes[1];
	else
		--pSizes[0];
}

template <class T> void CircBuf<T>::get_read_pointers( T *pPointers[2], unsigned pSizes[2] )
{
	const int rpos = read_pos;
	const int wpos = write_pos;

	if( rpos < wpos )
	{
		/* The buffer looks like "eeeeDDDDeeee" (e = empty, D = data). */
		pPointers[0] = buf+rpos;
		pPointers[1] = NULL;

		pSizes[0] = wpos - rpos;
		pSizes[1] = 0;
	}
	else if( rpos > wpos )
	{
		/* The buffer looks like "DDeeeeeeeeDD" (e = empty, D = data). */
		pPointers[0] = buf+rpos;
		pPointers[1] = buf;

		pSizes[0] = size - rpos;
		pSizes[1] = wpos;
	}
	else
	{
		/* The buffer looks like "eeeeeeeeeeee" (e = empty, D = data). */
		pPointers[0] = NULL;
		pPointers[1] = NULL;

		pSizes[0] = 0;
		pSizes[1] = 0;
	}
}

/* Write buffer_size elements from buffer, and advance the write pointer.  If
 * the data will not fit entirely, the write pointer will be unchanged
 * and false will be returned. */
template <class T> bool CircBuf<T>::write( const T *buffer, unsigned buffer_size )
{
	T *p[2];
	unsigned sizes[2];
	get_write_pointers( p, sizes );

	if( buffer_size > sizes[0] + sizes[1] )
		return false;
	
	const int from_first = min( buffer_size, sizes[0] );
	memcpy( p[0], buffer, from_first*sizeof(T) );
	if( buffer_size > sizes[0] )
		memcpy( p[1], buffer+from_first, max(buffer_size-sizes[0], 0u)*sizeof(T) );

	advance_write_pointer( buffer_size );

	return true;
}

/* Read buffer_size elements from buffer, and advance the read pointer.  If
 * the buffer can not be filled completely, the read pointer will be unchanged
 * and false will be returned. */
template <class T> bool CircBuf<T>::read( T *buffer, unsigned buffer_size )
{
	T *p[2];
	unsigned sizes[2];
	get_read_pointers( p, sizes );

	if( buffer_size > sizes[0] + sizes[1] )
		return false;

	const int from_first = min( buffer_size, sizes[0] );
	memcpy( buffer, p[0], from_first*sizeof(T) );
	if( buffer_size > sizes[0] )
		memcpy( buffer+from_first, p[1], max(buffer_size-sizes[0], 0u)*sizeof(T) );

	/* Set the data that we just read to 0xFF.  This way, if we're passing pointesr
	 * through, we can tell if we accidentally get a stale pointer. */
	memset( p[0], 0xFF, from_first*sizeof(T) );
	if( buffer_size > sizes[0] )
		memset( p[1], 0xFF, max(buffer_size-sizes[0], 0u)*sizeof(T) );

	advance_read_pointer( buffer_size );
	return true;
}
